﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SharpBolt.Tweening
{
    public delegate double EasingDelegate(double part);

    public class Easing
    {
        public static Dictionary<string, EasingDelegate> EasingDictionary { get; private set; }

        static Easing()
        {
            EasingDictionary = new Dictionary<string, EasingDelegate>();

            EasingDictionary.Add("Lin",     EaseLinear);
            EasingDictionary.Add("Back",    EaseInBack);
            EasingDictionary.Add("Sin",     EaseInSine);
            EasingDictionary.Add("Bnc",  EaseInBounce);
            EasingDictionary.Add("Circ",    EaseInCirc);
            EasingDictionary.Add("Cube",    EaseInCubic);
            EasingDictionary.Add("Elast",   EaseInElastic);
            EasingDictionary.Add("Exp",     EaseInExpo);
            EasingDictionary.Add("Quad",    EaseInQuad);
            EasingDictionary.Add("Qrt",   EaseInQuart);
            EasingDictionary.Add("Qnt",   EaseInQuint);
            EasingDictionary.Add("Bin", EaseBinary);

            EasingDictionary.Add("BackO", EaseOutBack);
            EasingDictionary.Add("SinO", EaseOutSine);
            EasingDictionary.Add("BncO", EaseOutBounce);
            EasingDictionary.Add("CircO", EaseOutCirc);
            EasingDictionary.Add("CubeO", EaseOutCubic);
            EasingDictionary.Add("ElastO", EaseOutElastic);
            EasingDictionary.Add("ExpO", EaseOutExpo);
            EasingDictionary.Add("QuadO", EaseOutQuad);
            EasingDictionary.Add("QrtO", EaseOutQuart);
            EasingDictionary.Add("QntO", EaseOutQuint);

            EasingDictionary.Add("BackIO", EaseInOutBack);
            EasingDictionary.Add("SinIO", EaseInOutSine);
            EasingDictionary.Add("BncIO", EaseInOutBounce);
            EasingDictionary.Add("CircIO", EaseInOutCirc);
            EasingDictionary.Add("CubeIO", EaseInOutCubic);
            EasingDictionary.Add("ElastIO", EaseInOutElastic);
            EasingDictionary.Add("ExpIO", EaseInOutExpo);
            EasingDictionary.Add("QuadIO", EaseInOutQuad);
            EasingDictionary.Add("QrtIO", EaseInOutQuart);
            EasingDictionary.Add("QntIO", EaseInOutQuint);
        }


        // ==================================================================================================================================
        // TWEENING EQUATIONS doubles -----------------------------------------------------------------------------------------------------
        // (the original equations are Robert Penner's work as mentioned on the disclaimer)

        ///<param name="t">Current time (in frames or seconds).</param>
        ///<param name="b">Starting value.</param>
        ///<param name="c">Change needed in value.</param>
        ///<param name="d">Expected easing duration (in frames or seconds).</param>
        ///<returns>The correct value.</returns> 
        public static double EaseLinear(double t, double b, double c, double d)
        {
            return c * t / d + b;
        }

        public static double EaseLinear(double part)
        {
            double t = part;
            double b = 0;
            double c = 1;
            double d = 1;
            return EaseLinear(t, b, c, d);
        }

        ///<param name="t">Current time (in frames or seconds).</param>
        ///<param name="b">Starting value.</param>
        ///<param name="c">Change needed in value.</param>
        ///<param name="d">Expected easing duration (in frames or seconds).</param>
        ///<returns>The correct value.</returns> 
        public static double EaseInQuad(double t, double b, double c, double d)
        {
            return c * (t /= d) * t + b;
        }

        public static double EaseInQuad(double part)
        {
            double t = part;
            double b = 0;
            double c = 1;
            double d = 1;
            return EaseInQuad(t, b, c, d);
        }

        ///<summary>EaseOutQuad</summary>                      
        ///<param name="t">Current time (in frames or seconds).</param>
        ///<param name="b">Starting value.</param>
        ///<param name="c">Change needed in value.</param>
        ///<param name="d">Expected easing duration (in frames or seconds).</param>
        ///<returns>The correct value.</returns> 
        public static double EaseOutQuad(double t, double b, double c, double d)
        {
            return -c * (t /= d) * (t - 2) + b;
        }

        public static double EaseOutQuad(double part)
        {
            double t = part;
            double b = 0;
            double c = 1;
            double d = 1;
            return EaseOutQuad(t, b, c, d);
        }
        ///<summary>EaseOutQuad</summary>                      
        ///<param name="t">Current time (in frames or seconds).</param>
        ///<param name="b">Starting value.</param>
        ///<param name="c">Change needed in value.</param>
        ///<param name="d">Expected easing duration (in frames or seconds).</param>
        ///<returns>The correct value.</returns> 
        public static double EaseInOutQuad(double t, double b, double c, double d)
        {
            if ((t /= d / 2) < 1) return c / 2 * t * t + b;
            return -c / 2 * ((--t) * (t - 2) - 1) + b;
        }

        public static double EaseInOutQuad(double part)
        {
            double t = part;
            double b = 0;
            double c = 1;
            double d = 1;
            return EaseInOutQuad(t, b, c, d);
        }
        ///<summary>EaseOutQuad</summary>                      
        ///<param name="t">Current time (in frames or seconds).</param>
        ///<param name="b">Starting value.</param>
        ///<param name="c">Change needed in value.</param>
        ///<param name="d">Expected easing duration (in frames or seconds).</param>
        ///<returns>The correct value.</returns> 
        public static double EaseOutInQuad(double t, double b, double c, double d)
        {
            if (t < d / 2) return EaseOutQuad(t * 2, b, c / 2, d);
            return EaseInQuad((t * 2) - d, b + c / 2, c / 2, d);
        }

        public static double EaseOutInQuad(double part)
        {
            double t = part;
            double b = 0;
            double c = 1;
            double d = 1;
            return EaseOutInQuad(t, b, c, d);
        }
        ///<summary>EaseInCubic</summary>                      
        ///<param name="t">Current time (in frames or seconds).</param>
        ///<param name="b">Starting value.</param>
        ///<param name="c">Change needed in value.</param>
        ///<param name="d">Expected easing duration (in frames or seconds).</param>
        ///<returns>The correct value.</returns> 
        public static double EaseInCubic(double t, double b, double c, double d)
        {
            return c * (t /= d) * t * t + b;
        }

        public static double EaseInCubic(double part)
        {
            double t = part;
            double b = 0;
            double c = 1;
            double d = 1;
            return EaseInCubic(t, b, c, d);
        }
        ///<summary>EaseOutCubic</summary>                      
        ///<param name="t">Current time (in frames or seconds).</param>
        ///<param name="b">Starting value.</param>
        ///<param name="c">Change needed in value.</param>
        ///<param name="d">Expected easing duration (in frames or seconds).</param>
        ///<returns>The correct value.</returns> 
        public static double EaseOutCubic(double t, double b, double c, double d)
        {
            return c * ((t = t / d - 1) * t * t + 1) + b;
        }

        public static double EaseOutCubic(double part)
        {
            double t = part;
            double b = 0;
            double c = 1;
            double d = 1;
            return EaseOutCubic(t, b, c, d);
        }
        ///<summary>EaseInOutCubic</summary>                      
        ///<param name="t">Current time (in frames or seconds).</param>
        ///<param name="b">Starting value.</param>
        ///<param name="c">Change needed in value.</param>
        ///<param name="d">Expected easing duration (in frames or seconds).</param>
        ///<returns>The correct value.</returns> 
        public static double EaseInOutCubic(double t, double b, double c, double d)
        {
            if ((t /= d / 2) < 1) return c / 2 * t * t * t + b;
            return c / 2 * ((t -= 2) * t * t + 2) + b;
        }

        public static double EaseInOutCubic(double part)
        {
            double t = part;
            double b = 0;
            double c = 1;
            double d = 1;
            return EaseInOutCubic(t, b, c, d);
        }
        ///<summary>EaseOutInCubic</summary>                      
        ///<param name="t">Current time (in frames or seconds).</param>
        ///<param name="b">Starting value.</param>
        ///<param name="c">Change needed in value.</param>
        ///<param name="d">Expected easing duration (in frames or seconds).</param>
        ///<returns>The correct value.</returns> 
        public static double EaseOutInCubic(double t, double b, double c, double d)
        {
            if (t < d / 2) return EaseOutCubic(t * 2, b, c / 2, d);
            return EaseInCubic((t * 2) - d, b + c / 2, c / 2, d);
        }

        public static double EaseOutInCubic(double part)
        {
            double t = part;
            double b = 0;
            double c = 1;
            double d = 1;
            return EaseOutInCubic(t, b, c, d);
        }

        ///<summary>EaseInQuart</summary>                      
        ///<param name="t">Current time (in frames or seconds).</param>
        ///<param name="b">Starting value.</param>
        ///<param name="c">Change needed in value.</param>
        ///<param name="d">Expected easing duration (in frames or seconds).</param>
        ///<returns>The correct value.</returns> 
        public static double EaseInQuart(double t, double b, double c, double d)
        {
            return c * (t /= d) * t * t * t + b;
        }

        public static double EaseInQuart(double part)
        {
            double t = part;
            double b = 0;
            double c = 1;
            double d = 1;
            return EaseInQuart(t, b, c, d);
        }
        ///<param name="t">Current time (in frames or seconds).</param>
        ///<param name="b">Starting value.</param>
        ///<param name="c">Change needed in value.</param>
        ///<param name="d">Expected easing duration (in frames or seconds).</param>
        ///<returns>The correct value.</returns> 
        public static double EaseOutQuart(double t, double b, double c, double d)
        {
            return -c * ((t = t / d - 1) * t * t * t - 1) + b;
        }

        public static double EaseOutQuart(double part)
        {
            double t = part;
            double b = 0;
            double c = 1;
            double d = 1;
            return EaseOutQuart(t, b, c, d);
        }
        ///<param name="t">Current time (in frames or seconds).</param>
        ///<param name="b">Starting value.</param>
        ///<param name="c">Change needed in value.</param>
        ///<param name="d">Expected easing duration (in frames or seconds).</param>
        ///<returns>The correct value.</returns> 
        public static double EaseInOutQuart(double t, double b, double c, double d)
        {
            if ((t /= d / 2) < 1) return c / 2 * t * t * t * t + b;
            return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
        }

        public static double EaseInOutQuart(double part)
        {
            double t = part;
            double b = 0;
            double c = 1;
            double d = 1;
            return EaseInOutQuart(t, b, c, d);
        }
        ///<param name="t">Current time (in frames or seconds).</param>
        ///<param name="b">Starting value.</param>
        ///<param name="c">Change needed in value.</param>
        ///<param name="d">Expected easing duration (in frames or seconds).</param>
        ///<returns>The correct value.</returns> 
        public static double EaseOutInQuart(double t, double b, double c, double d)
        {
            if (t < d / 2) return EaseOutQuart(t * 2, b, c / 2, d);
            return EaseInQuart((t * 2) - d, b + c / 2, c / 2, d);
        }

        public static double EaseOutInQuart(double part)
        {
            double t = part;
            double b = 0;
            double c = 1;
            double d = 1;
            return EaseOutInQuart(t, b, c, d);
        }
        ///<param name="t">Current time (in frames or seconds).</param>
        ///<param name="b">Starting value.</param>
        ///<param name="c">Change needed in value.</param>
        ///<param name="d">Expected easing duration (in frames or seconds).</param>
        ///<returns>The correct value.</returns> 
        public static double EaseInQuint(double t, double b, double c, double d)
        {
            return c * (t /= d) * t * t * t * t + b;
        }

        public static double EaseInQuint(double part)
        {
            double t = part;
            double b = 0;
            double c = 1;
            double d = 1;
            return EaseInQuint(t, b, c, d);
        }
        ///<param name="t">Current time (in frames or seconds).</param>
        ///<param name="b">Starting value.</param>
        ///<param name="c">Change needed in value.</param>
        ///<param name="d">Expected easing duration (in frames or seconds).</param>
        ///<returns>The correct value.</returns> 
        public static double EaseOutQuint(double t, double b, double c, double d)
        {
            return c * ((t = t / d - 1) * t * t * t * t + 1) + b;
        }

        public static double EaseOutQuint(double part)
        {
            double t = part;
            double b = 0;
            double c = 1;
            double d = 1;
            return EaseOutQuint(t, b, c, d);
        }
        ///<param name="t">Current time (in frames or seconds).</param>
        ///<param name="b">Starting value.</param>
        ///<param name="c">Change needed in value.</param>
        ///<param name="d">Expected easing duration (in frames or seconds).</param>
        ///<returns>The correct value.</returns> 
        public static double EaseInOutQuint(double t, double b, double c, double d)
        {
            if ((t /= d / 2) < 1) return c / 2 * t * t * t * t * t + b;
            return c / 2 * ((t -= 2) * t * t * t * t + 2) + b;
        }

        public static double EaseInOutQuint(double part)
        {
            double t = part;
            double b = 0;
            double c = 1;
            double d = 1;
            return EaseInOutQuint(t, b, c, d);
        }
        ///<param name="t">Current time (in frames or seconds).</param>
        ///<param name="b">Starting value.</param>
        ///<param name="c">Change needed in value.</param>
        ///<param name="d">Expected easing duration (in frames or seconds).</param>
        ///<returns>The correct value.</returns> 
        public static double EaseOutInQuint(double t, double b, double c, double d)
        {
            if (t < d / 2) return EaseOutQuint(t * 2, b, c / 2, d);
            return EaseInQuint((t * 2) - d, b + c / 2, c / 2, d);
        }

        public static double EaseOutInQuint(double part)
        {
            double t = part;
            double b = 0;
            double c = 1;
            double d = 1;
            return EaseOutInQuint(t, b, c, d);
        }

        ///<param name="t">Current time (in frames or seconds).</param>
        ///<param name="b">Starting value.</param>
        ///<param name="c">Change needed in value.</param>
        ///<param name="d">Expected easing duration (in frames or seconds).</param>
        ///<returns>The correct value.</returns> 
        public static double EaseInSine(double t, double b, double c, double d)
        {
            return -c * Math.Cos(t / d * (Math.PI / 2)) + c + b;
        }

        public static double EaseInSine(double part)
        {
            double t = part;
            double b = 0;
            double c = 1;
            double d = 1;
            return EaseInSine(t, b, c, d);
        }

        ///<param name="t">Current time (in frames or seconds).</param>
        ///<param name="b">Starting value.</param>
        ///<param name="c">Change needed in value.</param>
        ///<param name="d">Expected easing duration (in frames or seconds).</param>
        ///<returns>The correct value.</returns> 
        public static double EaseOutSine(double t, double b, double c, double d)
        {
            return c * Math.Sin(t / d * (Math.PI / 2)) + b;
        }

        public static double EaseOutSine(double part)
        {
            double t = part;
            double b = 0;
            double c = 1;
            double d = 1;
            return EaseOutSine(t, b, c, d);
        }

        ///<param name="t">Current time (in frames or seconds).</param>
        ///<param name="b">Starting value.</param>
        ///<param name="c">Change needed in value.</param>
        ///<param name="d">Expected easing duration (in frames or seconds).</param>
        ///<returns>The correct value.</returns> 
        public static double EaseInOutSine(double t, double b, double c, double d)
        {
            return -c / 2 * (Math.Cos(Math.PI * t / d) - 1) + b;
        }

        public static double EaseInOutSine(double part)
        {
            double t = part;
            double b = 0;
            double c = 1;
            double d = 1;
            return EaseInOutSine(t, b, c, d);
        }

        ///<param name="t">Current time (in frames or seconds).</param>
        ///<param name="b">Starting value.</param>
        ///<param name="c">Change needed in value.</param>
        ///<param name="d">Expected easing duration (in frames or seconds).</param>
        ///<returns>The correct value.</returns> 
        public static double EaseOutInSine(double t, double b, double c, double d)
        {
            if (t < d / 2) return EaseOutSine(t * 2, b, c / 2, d);
            return EaseInSine((t * 2) - d, b + c / 2, c / 2, d);
        }

        public static double EaseOutInSine(double part)
        {
            double t = part;
            double b = 0;
            double c = 1;
            double d = 1;
            return EaseOutInSine(t, b, c, d);
        }
        ///<param name="t">Current time (in frames or seconds).</param>
        ///<param name="b">Starting value.</param>
        ///<param name="c">Change needed in value.</param>
        ///<param name="d">Expected easing duration (in frames or seconds).</param>
        ///<returns>The correct value.</returns> 
        public static double EaseInExpo(double t, double b, double c, double d)
        {
            return (t == 0) ? b : c * Math.Pow(2, 10 * (t / d - 1)) + b - c * 0.001;
        }

        public static double EaseInExpo(double part)
        {
            double t = part;
            double b = 0;
            double c = 1;
            double d = 1;
            return EaseInExpo(t, b, c, d);
        }
        ///<param name="t">Current time (in frames or seconds).</param>
        ///<param name="b">Starting value.</param>
        ///<param name="c">Change needed in value.</param>
        ///<param name="d">Expected easing duration (in frames or seconds).</param>
        ///<returns>The correct value.</returns> 
        public static double EaseOutExpo(double t, double b, double c, double d)
        {
            return (t == d) ? b + c : c * 1.001 * (-Math.Pow(2, -10 * t / d) + 1) + b;
        }

        public static double EaseOutExpo(double part)
        {
            double t = part;
            double b = 0;
            double c = 1;
            double d = 1;
            return EaseOutExpo(t, b, c, d);
        }
        ///<param name="t">Current time (in frames or seconds).</param>
        ///<param name="b">Starting value.</param>
        ///<param name="c">Change needed in value.</param>
        ///<param name="d">Expected easing duration (in frames or seconds).</param>
        ///<returns>The correct value.</returns> 
        public static double EaseInOutExpo(double t, double b, double c, double d)
        {
            if (t == 0) return b;
            if (t == d) return b + c;
            if ((t /= d / 2) < 1) return c / 2 * Math.Pow(2, 10 * (t - 1)) + b - c * 0.0005;
            return c / 2 * 1.0005 * (-Math.Pow(2, -10 * --t) + 2) + b;
        }

        public static double EaseInOutExpo(double part)
        {
            double t = part;
            double b = 0;
            double c = 1;
            double d = 1;
            return EaseInOutExpo(t, b, c, d);
        }
        ///<param name="t">Current time (in frames or seconds).</param>
        ///<param name="b">Starting value.</param>
        ///<param name="c">Change needed in value.</param>
        ///<param name="d">Expected easing duration (in frames or seconds).</param>
        ///<returns>The correct value.</returns> 
        public static double EaseOutInExpo(double t, double b, double c, double d)
        {
            if (t < d / 2) return EaseOutExpo(t * 2, b, c / 2, d);
            return EaseInExpo((t * 2) - d, b + c / 2, c / 2, d);
        }

        public static double EaseOutInExpo(double part)
        {
            double t = part;
            double b = 0;
            double c = 1;
            double d = 1;
            return EaseOutInExpo(t, b, c, d);
        }

        ///<param name="t">Current time (in frames or seconds).</param>
        ///<param name="b">Starting value.</param>
        ///<param name="c">Change needed in value.</param>
        ///<param name="d">Expected easing duration (in frames or seconds).</param>
        ///<returns>The correct value.</returns> 
        public static double EaseInCirc(double t, double b, double c, double d)
        {
            return -c * (Math.Sqrt(1 - (t /= d) * t) - 1) + b;
        }

        public static double EaseInCirc(double part)
        {
            double t = part;
            double b = 0;
            double c = 1;
            double d = 1;
            return EaseInCirc(t, b, c, d);
        }
        ///<param name="t">Current time (in frames or seconds).</param>
        ///<param name="b">Starting value.</param>
        ///<param name="c">Change needed in value.</param>
        ///<param name="d">Expected easing duration (in frames or seconds).</param>
        ///<returns>The correct value.</returns> 
        public static double EaseOutCirc(double t, double b, double c, double d)
        {
            return c * Math.Sqrt(1 - (t = t / d - 1) * t) + b;
        }

        public static double EaseOutCirc(double part)
        {
            double t = part;
            double b = 0;
            double c = 1;
            double d = 1;
            return EaseOutCirc(t, b, c, d);
        }

        ///<param name="t">Current time (in frames or seconds).</param>
        ///<param name="b">Starting value.</param>
        ///<param name="c">Change needed in value.</param>
        ///<param name="d">Expected easing duration (in frames or seconds).</param>
        ///<returns>The correct value.</returns> 
        public static double EaseInOutCirc(double t, double b, double c, double d)
        {
            if ((t /= d / 2) < 1) return -c / 2 * (Math.Sqrt(1 - t * t) - 1) + b;
            return c / 2 * (Math.Sqrt(1 - (t -= 2) * t) + 1) + b;
        }

        public static double EaseInOutCirc(double part)
        {
            double t = part;
            double b = 0;
            double c = 1;
            double d = 1;
            return EaseInOutCirc(t, b, c, d);
        }

        ///<param name="t">Current time (in frames or seconds).</param>
        ///<param name="b">Starting value.</param>
        ///<param name="c">Change needed in value.</param>
        ///<param name="d">Expected easing duration (in frames or seconds).</param>
        ///<returns>The correct value.</returns> 
        public static double EaseOutInCirc(double t, double b, double c, double d, object p_params = null)
        {
            if (t < d / 2) return EaseOutCirc(t * 2, b, c / 2, d);
            return EaseInCirc((t * 2) - d, b + c / 2, c / 2, d);
        }

        public static double EaseOutInCirc(double part)
        {
            double t = part;
            double b = 0;
            double c = 1;
            double d = 1;
            return EaseOutInCirc(t, b, c, d);
        }

        ///<param name="t">Current time (in frames or seconds).</param>
        ///<param name="b">Starting value.</param>
        ///<param name="c">Change needed in value.</param>
        ///<param name="d">Expected easing duration (in frames or seconds).</param>
        ///<returns>The correct value.</returns> 
        public static double EaseInElastic(double t, double b, double c, double d, double amplitude = 0)
        {
            if (t == 0) return b;
            if ((t /= d) == 1) return b + c;
            double p = d * .3;// p_params.period;
            double s = 0;
            var a = amplitude;
            if (a < Math.Abs(c))
            {
                a = c;
                s = p / 4;
            }
            else
            {
                s = p / (2 * Math.PI) * Math.Asin(c / a);
            }
            return -(a * Math.Pow(2, 10 * (t -= 1)) * Math.Sin((t * d - s) * (2 * Math.PI) / p)) + b;
        }

        public static double EaseInElastic(double part)
        {
            double t = part;
            double b = 0;
            double c = 1;
            double d = 1;
            return EaseInElastic(t, b, c, d);
        }

        ///<param name="t">Current time (in frames or seconds).</param>
        ///<param name="b">Starting value.</param>
        ///<param name="c">Change needed in value.</param>
        ///<param name="d">Expected easing duration (in frames or seconds).</param>
        ///<returns>The correct value.</returns> 
        public static double EaseOutElastic(double t, double b, double c, double d, double amplitude = 0)
        {
            if (t == 0) return b;
            if ((t /= d) == 1) return b + c;
            double p = d * .3;// p_params.period;
            double s = 0;
            var a = amplitude;
            if (a < Math.Abs(c))
            {
                a = c;
                s = p / 4;
            }
            else
            {
                s = p / (2 * Math.PI) * Math.Asin(c / a);
            }
            return (a * Math.Pow(2, -10 * t) * Math.Sin((t * d - s) * (2 * Math.PI) / p) + c + b);
        }

        public static double EaseOutElastic(double part)
        {
            double t = part;
            double b = 0;
            double c = 1;
            double d = 1;
            return EaseOutElastic(t, b, c, d);
        }

        ///<param name="t">Current time (in frames or seconds).</param>
        ///<param name="b">Starting value.</param>
        ///<param name="c">Change needed in value.</param>
        ///<param name="d">Expected easing duration (in frames or seconds).</param>
        ///<returns>The correct value.</returns> 
        public static double EaseInOutElastic(double t, double b, double c, double d, double amplitude = 0)
        {
            if (t == 0) return b;
            if ((t /= d / 2) == 2) return b + c;
            double p = d * (.3 * 1.5);//p_params.period;
            double s = 0;
            var a = amplitude;
            if (a < Math.Abs(c))
            {
                a = c;
                s = p / 4;
            }
            else
            {
                s = p / (2 * Math.PI) * Math.Asin(c / a);
            }
            if (t < 1) return -.5 * (a * Math.Pow(2, 10 * (t -= 1)) * Math.Sin((t * d - s) * (2 * Math.PI) / p)) + b;
            return a * Math.Pow(2, -10 * (t -= 1)) * Math.Sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
        }

        public static double EaseInOutElastic(double part)
        {
            double t = part;
            double b = 0;
            double c = 1;
            double d = 1;
            return EaseInOutElastic(t, b, c, d);
        }

        ///<param name="t">Current time (in frames or seconds).</param>
        ///<param name="b">Starting value.</param>
        ///<param name="c">Change needed in value.</param>
        ///<param name="d">Expected easing duration (in frames or seconds).</param>
        ///<returns>The correct value.</returns> 
        public static double EaseOutInElastic(double t, double b, double c, double d)
        {
            if (t < d / 2) return EaseOutElastic(t * 2, b, c / 2, d);
            return EaseInElastic((t * 2) - d, b + c / 2, c / 2, d);
        }

        public static double EaseOutInElastic(double part)
        {
            double t = part;
            double b = 0;
            double c = 1;
            double d = 1;
            return EaseOutInElastic(t, b, c, d);
        }

        ///<param name="t">Current time (in frames or seconds).</param>
        ///<param name="b">Starting value.</param>
        ///<param name="c">Change needed in value.</param>
        ///<param name="d">Expected easing duration (in frames or seconds).</param>
        ///<returns>The correct value.</returns> 
        public static double EaseInBack(double t, double b, double c, double d, double overshoot = 1.70158)
        {
            var s = overshoot;
            return c * (t /= d) * t * ((s + 1) * t - s) + b;
        }

        public static double EaseInBack(double part)
        {
            double t = part;
            double b = 0;
            double c = 1;
            double d = 1;
            return EaseInBack(t, b, c, d);
        }

        ///<param name="t">Current time (in frames or seconds).</param>
        ///<param name="b">Starting value.</param>
        ///<param name="c">Change needed in value.</param>
        ///<param name="d">Expected easing duration (in frames or seconds).</param>
        ///<returns>The correct value.</returns> 
        public static double EaseOutBack(double t, double b, double c, double d, double overshoot = 1.70158)
        {
            var s = overshoot;
            return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
        }

        public static double EaseOutBack(double part)
        {
            double t = part;
            double b = 0;
            double c = 1;
            double d = 1;
            return EaseOutBack(t, b, c, d);
        }

        ///<param name="t">Current time (in frames or seconds).</param>
        ///<param name="b">Starting value.</param>
        ///<param name="c">Change needed in value.</param>
        ///<param name="d">Expected easing duration (in frames or seconds).</param>
        ///<returns>The correct value.</returns> 
        public static double EaseInOutBack(double t, double b, double c, double d, double overshoot = 1.70158)
        {
            var s = overshoot;
            if ((t /= d / 2) < 1) return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
            return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
        }

        public static double EaseInOutBack(double part)
        {
            double t = part;
            double b = 0;
            double c = 1;
            double d = 1;
            return EaseInOutBack(t, b, c, d);
        }

        ///<param name="t">Current time (in frames or seconds).</param>
        ///<param name="b">Starting value.</param>
        ///<param name="c">Change needed in value.</param>
        ///<param name="d">Expected easing duration (in frames or seconds).</param>
        ///<returns>The correct value.</returns> 
        public static double EaseOutInBack(double t, double b, double c, double d)
        {
            if (t < d / 2) return EaseOutBack(t * 2, b, c / 2, d);
            return EaseInBack((t * 2) - d, b + c / 2, c / 2, d);
        }


        public static double EaseOutInBack(double part)
        {
            double t = part;
            double b = 0;
            double c = 1;
            double d = 1;
            return EaseOutInBack(t, b, c, d);
        }

        ///<param name="t">Current time (in frames or seconds).</param>
        ///<param name="b">Starting value.</param>
        ///<param name="c">Change needed in value.</param>
        ///<param name="d">Expected easing duration (in frames or seconds).</param>
        ///<returns>The correct value.</returns> 
        public static double EaseInBounce(double t, double b, double c, double d)
        {
            return c - EaseOutBounce(d - t, 0, c, d) + b;
        }

        public static double EaseInBounce(double part)
        {
            double t = part;
            double b = 0;
            double c = 1;
            double d = 1;
            return EaseInBounce(t, b, c, d);
        }

        ///<param name="t">Current time (in frames or seconds).</param>
        ///<param name="b">Starting value.</param>
        ///<param name="c">Change needed in value.</param>
        ///<param name="d">Expected easing duration (in frames or seconds).</param>
        ///<returns>The correct value.</returns> 
        public static double EaseOutBounce(double t, double b, double c, double d)
        {
            if ((t /= d) < (1 / 2.75))
            {
                return c * (7.5625 * t * t) + b;
            }
            else if (t < (2 / 2.75))
            {
                return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
            }
            else if (t < (2.5 / 2.75))
            {
                return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
            }
            else
            {
                return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
            }
        }

        public static double EaseOutBounce(double part)
        {
            double t = part;
            double b = 0;
            double c = 1;
            double d = 1;
            return EaseOutBounce(t, b, c, d);
        }

        ///<param name="t">Current time (in frames or seconds).</param>
        ///<param name="b">Starting value.</param>
        ///<param name="c">Change needed in value.</param>
        ///<param name="d">Expected easing duration (in frames or seconds).</param>
        ///<returns>The correct value.</returns> 
        public static double EaseInOutBounce(double t, double b, double c, double d)
        {
            if (t < d / 2) return EaseInBounce(t * 2, 0, c, d) * .5 + b;
            else return EaseOutBounce(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
        }


        public static double EaseInOutBounce(double part)
        {
            double t = part;
            double b = 0;
            double c = 1;
            double d = 1;
            return EaseInOutBounce(t, b, c, d);
        }
        ///<param name="t">Current time (in frames or seconds).</param>
        ///<param name="b">Starting value.</param>
        ///<param name="c">Change needed in value.</param>
        ///<param name="d">Expected easing duration (in frames or seconds).</param>
        ///<returns>The correct value.</returns> 
        public static double EaseOutInBounce(double t, double b, double c, double d)
        {
            if (t < d / 2) return EaseOutBounce(t * 2, b, c / 2, d);
            return EaseInBounce((t * 2) - d, b + c / 2, c / 2, d);
        }

        public static double EaseOutInBounce(double part)
        {
            double t = part;
            double b = 0;
            double c = 1;
            double d = 1;
            return EaseOutInBounce(t, b, c, d);
        }

        public static double EaseBinary(double part)
        {
            return part < .5 ? 0 : 1;
        }

        public static EasingDelegate PingPong(EasingDelegate easingA, EasingDelegate easingB, double partAto1 = 0.5)
        {
            return PingPong(easingA, easingB, partAto1, 1 - partAto1);
        }

        public static EasingDelegate PingPong(EasingDelegate easingA, EasingDelegate easingB, double partA, double partB)
        {
            EasingDelegate result;

            double edge = partA / (partA + partB);
            result =
                part =>
                {
                    if (part < edge)
                    {
                        return easingA(part / edge);
                    }
                    else
                    {
                        return easingB(1 - ((part - edge) / (1 - edge)));
                    }
                };
            return result;
        }

        public static double PingPongLinear(double part)
        {           

            double edge = 0.5;

            if (part < edge)
            {
                return EaseLinear(part / edge);
            }
            else
            {
                return EaseLinear(1 - ((part - edge) / (1 - edge)));
            }
        }
    }
}
